#ifndef _SCENE_H_
#define _SCENE_H_

#include "texture.h"
#include <string>
#include <vector>

#define DROP_ACC 0.05f
#define ANIM_SPEED 4.0f
#define MOVE_SPEED 0.2f

class Entity{
private:
    double pos_x;
    double pos_y;
    double pos_z;
    double speed_x;
    double speed_y;
    double speed_z;
public:
    Entity(double x, double y, double z):pos_x(x), pos_y(y), pos_z(z), speed_x(0), speed_y(0), speed_z(0){};
    void setPosition(double x, double y, double z){
        this->pos_x = x;
        this->pos_y = y;
        this->pos_z = z;
    }
    std::vector<double> getPosition(void){
        return std::vector<double>{pos_x, pos_y, pos_z};
    }
    std::vector<double> getSpeed(void){
        return std::vector<double> {speed_x, speed_y, speed_z};
    }
    void setSpeed(double speed_x, double speed_y, double speed_z){
        this->speed_x = speed_x;
        this->speed_y = speed_y;
        this->speed_z = speed_z;
    }
};

class Box:public Entity{
private:
    int type;
public:
    Box(double x, double y, double z, int type):Entity(x, y, z){
        if(type >= 0 && type < 4) this->type = type;
        else this->type = 0;
    }
    int getType(void){return type;}
};

class Conveyor:public Entity{
private:
    int direction;
    int process;
public:
    Conveyor(int x, int y, int z, int dir):Entity(x, y, z){
        if(dir >= 0 && dir < 4)
            direction = dir;
        else direction = 0;
        process = 0;
    }
    int getDirection(void){return direction;}
    double getProcess(void){return process;}
    void animate(double speed_step){process += speed_step;}
    void rotate(bool clockwise){
        if(clockwise) direction += 1;
        else direction -= 1;
        if(direction == -1) direction = 3;
        else if(direction == 4) direction = 0;
    }
};

class Station:public Entity{
private:
    int id;
    bool running;
    bool color; // green(true) or red(false)
    bool state; // succeed(true) or failed(false)
    double process;
public:
    Station(int x, int y, int z, int id):Entity(x, y, z){
        this->id = id;
        running = false;
        state = false;
        color = false;
        process = 0;
    }
    int getID(void){return id;}
    bool getColor(void){return color;}
    bool checkStatus(void){return running;}
    bool checkResult(void){return state;}
    void restart(void){
        if(!running){
            running = true;
            state = false;
        }else return;
    }
    void finish(bool status){
        running = false;
        state = status;
        color = status;
    }
    void animate(double speed_step){
        if(running){
            process += speed_step;
            if((int)process % 100 == 0)
                color = !color;
        }
    }
};

class Scene{
private:
    TextureManager textures;
    std::vector<Box> box_list;
    std::vector<Conveyor> conveyor_list;
    std::vector<Station> station_list;
    bool is_running = false;
    bool is_succeed = false;
    bool is_ready = true;
    bool is_pushing = true;
    void drawQuadFace(std::string tex, double pts[4][3], double tex_cood[4][2]);
    void drawQuad(std::string tex_left, std::string tex_right, std::string tex_front, std::string tex_back,
                std::string tex_up, std::string tex_down, double tex_repeat_x, double tex_repeat_y);
    void drawPlatform(int size);
    void calcOutletTexCood(double pts_cood[4][3], double tex_cood[4][2]);
    void drawOutlet(double pos_x, double pos_y, double pos_z);
    void drawBelt(double proc);
    void drawConveyor(int pos_x, int pos_y, int pos_z, int dir, double proc);
    void drawStation(int pos_x, int pos_y, int pos_z, bool status);
    void drawBox(int box_type, double pos_x, double pos_y, double pos_z);
    bool hitEntity(int box_index, double x, double y, double z);
    bool isOnTop(double x, double y, int z);
    bool isSucceed(void);
    bool isArrived(int box_index);
    bool isTransporting(int box_index);
    bool isDroping(int box_index);
    void step(int box_index);
    void simulate(void);
public:
    void init(void);
    void draw(void);
    void reset(void);
    void clear(void);
    void start(void);
    void abort(void);
    bool addConveyor(int x, int y, int z, int dir);
    bool delConveyor(int x, int y, int z);
    bool rotConveyor(int x, int y, int z, bool clockwise);
    bool checkCameraHit(double x, double y, double z);
    bool checkObjectHit(int x, int y, int z);
    bool getStatus(void){return is_running;}
    bool getResult(void){return is_succeed;}
};

#endif